昨天我們看過了 val customer = call.receive<Customer>()
這段函數,在 Ktor 裡面是怎麼將 HTML 請求轉換成自定義的 Customer
物件。
今天我們來看看,在處理回傳的時候,Ktor 又是如何將自定義物件轉換成 HTML 回傳的。
在 Ktor 的教學裡面,如果我們要試著取出 Customer
並回傳,寫法如下
get("{id?}") {
val id = call.parameters["id"] ?: return@get call.respondText(
"Missing id",
status = HttpStatusCode.BadRequest
)
val customer =
customerStorage.find { it.id == id } ?: return@get call.respondText(
"No customer with id $id",
status = HttpStatusCode.NotFound
)
call.respond(customer)
}
前面先透過 call.parameters["id"]
取得 id,我們來看看 parameters
的實作
/**
* Parameters associated with this call.
*/
public val parameters: Parameters
/**
* Represents HTTP parameters as a map from case-insensitive names to collection of [String] values
*/
public interface Parameters : StringValues {
public companion object {
/**
* Empty [Parameters] instance
*/
public val Empty: Parameters = EmptyParameters
/**
* Builds a [Parameters] instance with the given [builder] function
* @param builder specifies a function to build a map
*/
public inline fun build(builder: ParametersBuilder.() -> Unit): Parameters =
ParametersBuilder().apply(builder).build()
}
}
這邊我們看到使用了 ParametersBuilder
來進行 parameters
的處理
至於 respond
的實作則是如下
/**
* Sends a [message] as a response.
* @see [io.ktor.server.response.ApplicationResponse]
*/
@OptIn(InternalAPI::class)
@JvmName("respondWithType")
public suspend inline fun <reified T : Any> ApplicationCall.respond(message: T) {
if (message !is OutgoingContent && message !is ByteArray) {
response.responseType = typeInfo<T>()
}
response.pipeline.execute(this, message as Any)
}
前面先將 typeInfo
設置成了輸入的 T
,在我們這邊的範例來說,就是 Customer
類別
這邊的 response.pipeline.execute(this, message as Any)
跟昨天我們看到的 val transformed = request.pipeline.execute(this, incomingContent)
似乎有點相近
我們分別來看一下這兩段程式
昨天的 request.pipeline
是
/**
* A pipeline for receiving content.
*/
public val pipeline: ApplicationReceivePipeline
public open class ApplicationReceivePipeline(
override val developmentMode: Boolean = false
) : Pipeline<Any, ApplicationCall>(Before, Transform, After)
今天的 response.pipeline
則是
/**
* A pipeline for sending content.
*/
public val pipeline: ApplicationSendPipeline
/**
* Server response send pipeline.
*/
public open class ApplicationSendPipeline(
override val developmentMode: Boolean = false
) : Pipeline<Any, ApplicationCall>(Before, Transform, Render, ContentEncoding, TransferEncoding, After, Engine)
到這邊,我們可以發現,這裡共同繼承了 Pipeline
類別,所以不需要分別撰寫階段處理請求或者回傳的邏輯。我們就可以用類似的寫法,來將請求轉換成程式自己定義的類別,或者將自己定義的類別轉換成回傳。
另外,透過這樣撰寫的方式,我們可以在不需要特別撰寫新程式的狀態下,讓套件可以參與轉換請求或者轉換回傳的邏輯。
這樣一來,如果我們希望回傳的格式不一樣,比方說我們想改成回傳 xml
只需要在安裝對應套件後,這樣調整程式
fun Application.configureSerialization() {
install(ContentNegotiation) {
xml()
}
}
裡面就會改成
/**
* Registers the `application/xml` (or another specified [contentType]) content type
* to the [ContentNegotiation] plugin using kotlinx.serialization.
*
* You can learn more from [Content negotiation and serialization](https://ktor.io/docs/serialization.html).
*
* @param format instance. [DefaultXml] is used by default
* @param contentType to register with, `application/xml` by default
*/
public fun Configuration.xml(
format: XML = DefaultXml,
contentType: ContentType = ContentType.Application.Xml
) {
serialization(contentType, format)
}
然後取得 XML
物件
/**
* The default XML configuration. The settings are:
* - Every declaration without a namespace is automatically wrapped in the namespace.
* See also [XMLOutputFactory.IS_REPAIRING_NAMESPACES].
* - The XML declaration is not generated.
* - The indent is empty.
* - Polymorphic serialization is disabled.
*
* See [XML] for more details.
*/
public val DefaultXml: XML = XML {
repairNamespaces = true
xmlDeclMode = XmlDeclMode.None
indentString = ""
autoPolymorphic = false
this.xmlDeclMode
}
@OptIn(ExperimentalSerializationApi::class, ExperimentalXmlUtilApi::class)
public class XML constructor(
public val config: XmlConfig,
serializersModule: SerializersModule = EmptySerializersModule()
) : StringFormat
然後成功的將請求與回傳改成用 XML 的形式進行處理。
到今天,我們看過了 Ktor 如何安裝 ContentNegotiation
,並可以自由地調整解析的設定
讓我們可以將自定義的類別,與 HTML 請求和回傳,自由的進行轉換。
這半個月希望看的人都有一些收穫,今天我們就先看到這邊。